package net.halflite.mzkr.service;

import static net.halflite.mzkr.entity.names.UserImageNames.id;
import static net.halflite.mzkr.entity.names.UserImageNames.registedUser;
import static net.halflite.mzkr.entity.names.UserImageNames.registedUserId;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import javax.imageio.ImageIO;

import net.halflite.mzkr.dto.user.ImageDto;
import net.halflite.mzkr.entity.UserImage;
import net.halflite.mzkr.type.ImageExtensionType;
import net.halflite.mzkr.type.ImageSizeType;

import org.apache.commons.io.IOUtils;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.upload.FormFile;
import org.seasar.extension.jdbc.where.SimpleWhere;
import org.seasar.framework.beans.util.Beans;

public class UserImageService extends AbstractService<UserImage> {

	//------------------------------------------------------------ [アップロード]

	/** ファイル検証 */
	public ActionMessages validateFormFile(FormFile formFile) {
		ActionMessages result = new ActionMessages();
		int fileSize = formFile.getFileSize() / 1024;
		if (fileSize > UserImage.SIZE_MAX) {
			result.add("formFile", new ActionMessage("errors.upload.size", fileSize, UserImage.SIZE_MAX));
		}
		String contentType = formFile.getContentType();
		if (ImageExtensionType.contentToExtension(contentType) == null) {
			result.add("formFile", new ActionMessage("errors.upload.type"));
		}
		return result;
	}

	/** アップロード */
	public ImageDto upload(FormFile formFile) throws FileNotFoundException, IOException {
		ImageDto dto = new ImageDto();
		ImageExtensionType ext = ImageExtensionType.contentToExtension(formFile.getContentType());
		dto.image = cutAsSquare(ImageIO.read(formFile.getInputStream()), ext, ImageSizeType.LARGE);
		dto.ext = ext;
		dto.modified = new Date();
		return dto;
	}
	
	/** DB登録 */
	public void post(long userId, ImageDto dto) throws IOException {
	    UserImage userImage = select()
    		.where(new SimpleWhere().eq(registedUserId(), userId))
    		.getSingleResult();
	    if (userImage == null) userImage = new UserImage(userId);
	    
	    userImage.bytes = imageToBytes(dto);
	    userImage.ext = dto.ext;
	    if (userImage.id == 0) {
	    	insert(userImage);
	    } else {
	    	userImage.modified = new Date();
	    	update(userImage);
	    }
	}

	//------------------------------------------------------------ [画像表示]

	/** アイコン画像データ読み出し(該当テーブルが無いときはnullが返る) */
	public ImageDto findImage(String username, ImageSizeType size, long imageId) throws IOException {
		// ImageDto dto = new ImageDto();
	    UserImage userImage = select()
	    	.innerJoin(registedUser())
	    	.where(new SimpleWhere()
	    		.eq(registedUser().username(), username)
	    		.eq(id(), imageId))
	    	.getSingleResult();
	    if (userImage == null) return null;
		
	    ImageDto dto = Beans.createAndCopy(ImageDto.class, userImage).execute();
	    InputStream in = new ByteArrayInputStream(userImage.bytes);
	    BufferedImage image = ImageIO.read(in);
	    IOUtils.closeQuietly(in);
		dto.image = resize(image, dto.ext, size);
		return dto;
	}
	
	
	/** 画像を正方形に切り出す */
	private BufferedImage cutAsSquare(BufferedImage image, ImageExtensionType ext, ImageSizeType type) {
		int pixel = type.getPixel();
		int orgWidth = image.getWidth();
		int orgHeight = image.getHeight();
		int w = (orgWidth > orgHeight) ? (orgWidth * pixel / orgHeight) : pixel;
		
		BufferedImage resized = createDefaultSquareImage(image, ext, pixel);
		Image scaled = image.getScaledInstance(w, -1, Image.SCALE_SMOOTH);
		int x = (pixel - scaled.getWidth(null)) / 2;
		int y = (pixel - scaled.getHeight(null)) / 2;
		resized.getGraphics().drawImage(scaled, x, y, null);
		
		return resized;
	}
	
	/** 正方形画像を縮小 */
	private BufferedImage resize(BufferedImage image, ImageExtensionType ext, ImageSizeType type) {
		int pixel = type.getPixel();
		BufferedImage resized = createDefaultSquareImage(image, ext, pixel);
		Image scaled = image.getScaledInstance(pixel, pixel, Image.SCALE_SMOOTH);
		resized.getGraphics().drawImage(scaled, 0, 0, null);
		return resized;
	}
	
	private BufferedImage createDefaultSquareImage(BufferedImage image, ImageExtensionType ext, int pixel) {
		// PNGだと画像形式がTYPE_CUSTOMになるのを回避する処理
		int imageType = (ext == ImageExtensionType.PNG) ? BufferedImage.TYPE_INT_ARGB : image.getType();
		return new BufferedImage(pixel, pixel, imageType);
	}
	
	/** BufferedImageをbyteの配列に変換 */
	private byte[] imageToBytes(ImageDto dto) throws IOException {
	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
	    BufferedOutputStream os = new BufferedOutputStream(bos);
	    ImageIO.write(dto.image, dto.ext.getExt(), os);
	    os.flush();
	    IOUtils.closeQuietly(os);
	    return bos.toByteArray();
	}
	
}
